/*
 * Project: DroidAttack
 * Copyright (C) 2009 Jeffrey Rusterholz <dataforger@gmail.com>
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */ 

package com.google.code.droidattack;

import java.io.Serializable;
import java.util.ArrayList;
import android.util.Log;

/**
 * 
 * @author Jeffrey Rusterholz
 * 
 */
public class Galaxy implements Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = -3721772743789263336L;

	private static final String TAG = "DA Galaxy";
	
	public int mWidth = 7;
	public int mHeight = 6;
	public ArrayList<Star> mStars = new ArrayList<Star>();   // The stars in the galaxy.
	
	/**
	 * Create the star field with width and height.
	 * @param w
	 * @param h
	 */
	public Galaxy(int w, int h) {
		int id, max;
		Star star;
        		
		mWidth  = 7;
		mHeight = 6;
	
		// Width and height for a star.
		int xsize = w / mWidth;
        int ysize = h / mHeight;
		
		// Maximum nr of stars.
		max = mWidth * mHeight;
		id = 0;
		for (int i = 0; i < max; i++) {
			star = new Star();
        	star.id = id;
        	star.xoffset = (int) Math.round(Math.random() * ((xsize - 8) / 2));
        	star.yoffset = (int) Math.round(Math.random() * ((ysize - 8) / 2));
        	star.mCol = i % mWidth;
        	star.mRow = i / mWidth;
			
			mStars.add(star);
			id++;
		}
		setStarCoordinates(w, h);
		initGalaxy();

		// Player base star.
		int stars[] = new int[] {0, 1, mWidth};
		int i = (int) Math.round(Math.random() * 2); 
		star = mStars.get(stars[i]);
		star.owner = Star.OWNER_PLAYER1;	
		star.type = Star.TYPE_HOME;
		star.OccupationCount = 1;
		star.mDroids = 3;

		// Computer home star.
		stars = new int[] {mStars.size() - 1, mStars.size() - 2, mStars.size() - mWidth -1};
		i = (int) Math.round(Math.random() * 2); 
		star = mStars.get(mStars.size() - 1);
		star.owner = Star.OWNER_PLAYER2;
		star.type = Star.TYPE_HOME;
		star.OccupationCount = 1;
		star.mDroids = 3;

		addSpaceStations();
		createWormholes();
	}
	

	/**
	 * Calculate the x and y positions of the stars.
	 */
	public void setStarCoordinates(int w, int h) {
		int xsize = w / mWidth;
		int ysize = h / mHeight;
		for (Star s: mStars) {
        	s.mX = 4 + s.mCol * xsize;
        	s.mY = 1 + s.mRow * ysize;
		}
	}

	/**
	 * Initialize galaxy.
	 * Remove some stars.
	 */
	private void initGalaxy() {
		int n = 6 + (int) Math.round(Math.random() * 12);
		int i;
		
		while (n > 0) {
			i = (int) Math.round(Math.random() * (mStars.size() - 1));
			mStars.remove(i);
			n--;
		}
		Log.i(TAG , "Galaxy intialized.");
	}
	
	/**
	 * Change some stars into space stations.
	 */
	private void addSpaceStations() {
		int i, n;

		n = (int) Math.round(Math.random() * 5); // Max 5 stations.
		while (n > 0) {
			i = (int) Math.round(Math.random() * (mStars.size() - 1));
			if (mStars.get(i).type != Star.TYPE_STATION) {
				mStars.get(i).type = Star.TYPE_STATION;
				n--;
			}
		}		
	}
	
	/**
	 * Recursively search for nearby star.
	 * @param star
	 * @param ColDelta
	 * @param RowDelta
	 * @param CounterDelta
	 * @return Star
	 */
	private Star searchStar(Star star, int ColDelta, int RowDelta, int counter) {
		
		if (counter > 2) return null; // Max recursive depth.
		
		int col = star.mCol + ColDelta * counter;
		int row = star.mRow + RowDelta * counter;

		// Within the boundaries?
		if (col < 0 | col >= mWidth | row < 0 | row >= mHeight)
			return null;
		int id = row * mWidth + col;
		Star star2 = findStar(id);
		Log.d(TAG, "Searching star " + id);
		if (star2 == null) 
			star2 = searchStar(star, ColDelta, RowDelta, counter + 1);
		return star2;
	}
	
	/**
	 * Increase the count of stars occupied by player.
	 * @param player
	 * @param turns
	 */
	public void increaseOccupation(int player, int turns) {
		for (Star s: mStars) {

			if (s.owner == player & s.OccupationCount > 0) {
				
				// Increase home base droids first 5 turns.
				if (s.type == Star.TYPE_HOME & (turns > 0 & turns < 6)) {
					s.OccupationCount = 4;
				}
				
				// Increase droids every 4 turns.
				s.OccupationCount++;			
				if (s.OccupationCount > 4) {
					if (s.mDroids < 9) s.mDroids++;					
					s.OccupationCount = 1;
				}
			}
		}
	}
	
	/**
	 * Create the worm holes between the stars.
	 * TODO: This doesn't work always. Need better algorithm...
	 */
	private void createWormholes() {
		// Array to find surrounding stars. 
		int pos[][] = new int[][] { {-1, -1}, { 0, -1}, { 1, -1},
				                    {-1,  0},           { 1,  0},
				                    {-1,  1}, { 0,  1}, { 1,  1} };

		for (Star star: mStars) {
			if (star.ConnectedTo.size() >= 2) continue;

			// Max. number of connections, at least 1.
			int nc = 1 + (int) Math.round(Math.random() * 4);
			
			// Connect to at least 1 surrounding star.
			for (int i = 0; i < pos.length & nc > 0; i++) {
				// Determine the id of the surrounding star.
				Star star2 = searchStar(star, pos[i][0], pos[i][1], 1);

				// Star found and has not yet max. wormholes?
				if (star2 != null) {
					if (star2.ConnectedTo.size() < 4) {
						// Not already connected?
						if (star.ConnectedTo.indexOf(star2.id) < 0) {
							star.ConnectedTo.add(star2.id);
							star2.ConnectedTo.add(star.id);
							nc--;									
						}
					}
				}				
			}
		}
		Log.i(TAG , "Wormholes created.");
	}

	/**
    * Find star by ID.
    * @param id
    * @return Star
    */
   public Star findStar(int id) {
      Star star = null;
      for (Star s: mStars) {
         if (s.id == id) {
            star = s;
            break;
         }
      }
      return star;
   }

}
